﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using VideoLan;
using System.Web;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Threading;
using System.Net.Sockets;
using System.Net;

namespace MulticastTV
{
    public partial class Form1 : Form
    {
        Thread Tstop;

        ManualResetEvent forcestop = new ManualResetEvent(true);
        ManualResetEvent stopped = new ManualResetEvent(true);

        int streamInfoCheckCount = 0;
        
        #region Vlc Methods

        // change channel from seperate thread
        Thread uniChange = null;

        // current selected pids
        ChanSettings curSettings = new ChanSettings();

        /// <summary>
        /// // 0.5 second timer event handler which updates number of audio and subtitle tracks.
        /// </summary>
        private void TimerStreamInfo_Tick(object sender, EventArgs e)
        {
             upddateStreamInfo();
        }

        /// <summary>
        /// Vlc global arguments
        /// </summary>
        public void loadArgs()
        {
            string hyuv = "";
            string vout = "";
            string miface = "";
            string brightness, contrast, gamma, saturation;
            string screensaver = "";
            string PrefAudioLang = "";
            string PrefSubtLang = "";
            string SubX = "";
            string SubY = "";

            screensaver = "--no-disable-screensaver";

            brightness = ((float)Properties.Settings.Default.PictureBrightnessV1 / 100.0f).ToString().Replace(",", ".");
            contrast = ((float)Properties.Settings.Default.PictureContrastV1 / 100.0f).ToString().Replace(",", ".");
            gamma = ((float)Properties.Settings.Default.PictureGammaV1 / 100.0f).ToString().Replace(",", ".");
            saturation = ((float)Properties.Settings.Default.PictureSaturationV1 / 100.0f).ToString().Replace(",", ".");

            if (Globals.determineInterfaceByIndex)
            {
                miface = "--miface=" + Globals.interfaceindex;
            }
            else
            {
                if (Globals.interfaceip != "")
                    miface = "--miface-addr=" + Globals.interfaceip;
                else
                    miface = "";
            }

            if (Properties.Settings.Default.VideoHardwareYuv)
                hyuv = "--directx-hw-yuv";
            else
                hyuv = "--no-directx-hw-yuv";

            // video renderer
            if (Properties.Settings.Default.VideoOutV3 == "Directx")
                vout = "--vout=directx";
            else if (Properties.Settings.Default.VideoOutV3 == "Direct3d")
                vout = "--vout=direct3d";
            else if (Properties.Settings.Default.VideoOutV3 == "OpenGL")
                vout = "--vout=opengl";
            else
                vout = "";

            // audio settings
            string audioout;
            string dolbyprologic = "";
            string normalize;
            string spdifpass;
            string sync;

            if (Properties.Settings.Default.AudioAPI == "waveout")
                audioout = "--aout=waveout";
            else
                audioout = "";
            if (Properties.Settings.Default.AudioDolbyProLogic == "auto")
                dolbyprologic = "--force-dolby-surround=0";
            else if (Properties.Settings.Default.AudioDolbyProLogic == "on")
                dolbyprologic = "--force-dolby-surround=1";
            else
                dolbyprologic = "--force-dolby-surround=2";
            if (Properties.Settings.Default.AudioNormalize == true)
                normalize = "--audio-filter=normvol";
            else
                normalize = "";
            if (Properties.Settings.Default.AudioSPDIFPass == true)
                spdifpass = "--spdif";
            else
                spdifpass = "";
            if (Properties.Settings.Default.AudioSync != 0)
                sync = "--audio-desync=" + Properties.Settings.Default.AudioSync.ToString();
            else
                sync = "";

            // audio / sub language
            if (Properties.Settings.Default.AudioPrefLang.Length >= 2)
                PrefAudioLang = "--audio-language="+Properties.Settings.Default.AudioPrefLang;
            if (Properties.Settings.Default.SubtitlePrefLang.Length >= 2)
                PrefSubtLang = "--sub-language="+Properties.Settings.Default.SubtitlePrefLang;

            // sub position
            if (Properties.Settings.Default.SubtitleYPos != 0)
            {
                SubX = "--dvbsub-x=0";
                SubY = "--dvbsub-y="+ Math.Abs(Properties.Settings.Default.SubtitleYPos);
            }

           Globals.vlcargs = new string[] {
                screensaver,
                "--no-one-instance",
                "--no-loop",
                "--autocrop-ratio-max="+Properties.Settings.Default.PictureAutoMaxCropV2.ToString(),
                "--autocrop-diff=30",
                "--autocrop-luminance-threshold=18",
                "--custom-crop-ratios=67:100,1:1,4:3,14:9,48:35,5:3,180:100,185:100,235:100,1333:1000,1555:1000,1777:1000,1666:1000,1850:1000,2350:1000",
                "--custom-aspect-ratios=14:9,16:10",
                "--drop-late-frames",
                vout,
                //"--sout-all",
                //"--no-sout-keep",
                "--ffmpeg-skiploopfilter="+Properties.Settings.Default.VideoSkipLoopFilterV2.ToString(),
                "--deinterlace-mode="+Properties.Settings.Default.VideoDeinterlaceV4,
                "--ignore-config",
                //"--plugin-path=" +  Path.Combine(path,"plugins"),
                "--no-osd",
                miface,
                "--udp-caching="+Properties.Settings.Default.UdpCache,
                "--audio-replay-gain-mode=album",
                audioout,
                dolbyprologic,
                normalize,
                spdifpass,
                sync,
                "--brightness="+brightness,
                "--contrast="+contrast,
                "--saturation="+saturation,
                "--gamma="+gamma,
                PrefAudioLang,
                PrefSubtLang,
                SubX,                                   //CAN BE MOVED ONLY DOWN 
                SubY,
               //"-vvv",
               "--quiet",
               "--no-plugins-cache", // otherwise conflicts with vlc instalation
               hyuv,
                };
        }

        /// <summary>
        /// Player instance specific arguments
        /// </summary>
        private void playerArguments(ref VlcMedia media, ChannelData cd)
        {
            // gui:
            uncheckMask();
            uncheckZoom();
            
            string deinterlace = "";
            bool deinterlaceEnabled;
            string aspectratio = "";
            string videofilter = "";
            string zoom = "";
            string d3dvertpos = "";
            string overlay = "";

            if (Properties.Settings.Default.VideoOutV3 == "Direct3d")
            {
                overlay = ":no-overlay";
            }

            if (Properties.Settings.Default.VideoDeinterlaceV4 == "")
                deinterlaceEnabled = false;
            else
                deinterlaceEnabled = true;

            // Override global settings with per channel settings.
            // 1. Check if this feature is enabled.
            if (Properties.Settings.Default.PerChEnabled && ChannelSettings.List.Count > 0)
            {
                // Do we have setting for this channel.
                int index = ChannelSettings.List.FindIndex(ChannelSettings.byNameAndNumber(cd.Num, cd.Name));

                if (index != -1)
                {
                    ChannelSetting set = ChannelSettings.List[index];

                    // Override deinterlice.
                    if (Properties.Settings.Default.PerChDeinterlace)
                    {
                        if (set.Deinterlace)
                            deinterlaceEnabled = true;
                        else
                            deinterlaceEnabled = false;
                    }

                    // Override aspect ratio.
                    if (Properties.Settings.Default.PerChAspectRatio)
                    {
                        curar = set.AspectRatio;
                        // gui:
                        uncheckAr();
                        arGuiUpdate(curar);
                    }

                    // Override volume.
                    if (Properties.Settings.Default.PerChVolume)
                    {
                        volume = set.Volume;
                    }

                    //Override Aidio Pid.
                    if (Properties.Settings.Default.PerChAudioPid)
                    {
                        if (set.AudioPid != -1)
                            curSettings.AudioPID = set.AudioPid;
                    }

                    //Override Subtitles Pid.
                    if (Properties.Settings.Default.PerChSubtitlePid)
                    {
                        if (set.SubtitlePid != -1)
                            curSettings.SubtitlesPID = set.SubtitlePid;
                    }

                    // zoom, offset, mask
                    if (Properties.Settings.Default.PerChZoomRatio) 
                    {
                        // zoom
                        curSettings.Zoom = set.Zoom;
                        zoom = set.Zoom.ToString(System.Globalization.NumberFormatInfo.InvariantInfo);
                        zoomGuiUpdate(null, set.Zoom, false); // gui update

                        // position ( has no gui )
                        curSettings.VertOffset = set.VertOffset;
                        if (VoutModule=="Direct3d") // 
                            d3dvertpos = set.VertOffset.ToString();

                        // mask
                        curSettings.Mask = set.MaskRatio;
                        maskGuiUpdate(set.MaskRatio); //gui update
                    }
                    else
                        resetPictDimensions();
                }
                else
                    resetPictDimensions();
            }
            else
                resetPictDimensions();
            
            //aspect ratio
            aspectratio = ":aspect-ratio=" + curar;

            //no deinterlace
            if (!deinterlaceEnabled)
            {
                deinterlace = "";
                //brightness contrast..
                if (Properties.Settings.Default.PictureBrightnessV1 != 100 || Properties.Settings.Default.PictureContrastV1 != 100 || Properties.Settings.Default.PictureGammaV1 != 100 || Properties.Settings.Default.PictureSaturationV1 != 100)
                    videofilter = ":video-filter=adjust:deinterlace";
            }
            //deinterlace
            else
            {
                //brightness contrast, gama, saturation
                if (Properties.Settings.Default.PictureBrightnessV1 != 100 || Properties.Settings.Default.PictureContrastV1 != 100 || Properties.Settings.Default.PictureGammaV1 != 100 || Properties.Settings.Default.PictureSaturationV1 != 100)
                {
                    //direct3d and opengl : adjust and deinterlace in video-filter
                    if (Properties.Settings.Default.VideoOutV3 == "Direct3d" || Properties.Settings.Default.VideoOutV3 == "OpenGL")
                    {
                        deinterlace = ":video-filter=adjust:deinterlace";
                    }
                    //directx : adjust in video-filter, deinterlace in vout-filter
                    else
                    {
                        videofilter = ":video-filter=adjust";
                        deinterlace = ":vout-filter=deinterlace";
                    }
                }
                else
                    deinterlace = ":vout-filter=deinterlace";
            }

            // add options
            media.AddOption(System.Text.Encoding.UTF8.GetBytes(":d3d-resizer="+Properties.Settings.Default.VideoD3dTextureFilter));// for future use : bicubic resizer - pixel shader

            if (aspectratio.Length > 0)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(aspectratio));
            if (deinterlace.Length > 0)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(deinterlace));
            if (videofilter.Length > 0)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(videofilter));
            if (curSettings.AudioPID != -1)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(":audio-track-id=" + curSettings.AudioPID.ToString()));
            if (curSettings.SubtitlesPID != -1)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(":sub-track-id=" + curSettings.SubtitlesPID.ToString()));
            if (zoom.Length > 0)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(":scale=" + zoom));
            if (overlay.Length>0)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(overlay));
            if (d3dvertpos.Length>0)
                media.AddOption(System.Text.Encoding.UTF8.GetBytes(":d3d-movepicture="+d3dvertpos));
            pictureDimensions();
        }


        private void resetPictDimensions()
        {
            curSettings.Mask = 1.0f;
            curSettings.Zoom = 1.0f;
            curSettings.VertOffset = 0;
            czoom = 0;
            cmask = 0;
        }

        /// <summary>
        /// Save or update current channel settings into a list
        /// </summary>
        private void savePerChanSettings(ChannelData cd)
        { 
            // 1. check if this feature is enabled
            if (Properties.Settings.Default.PerChEnabled)
            {
                ChannelSetting cs = new ChannelSetting();
                cs.ChannelName = cd.Name;
                cs.ChannelNumber = cd.Num;
                cs.MaskRatio = curSettings.Mask;
                cs.AspectRatio = curar;
                cs.Volume = volume;
                cs.AudioPid = curSettings.AudioPID;
                cs.SubtitlePid = curSettings.SubtitlesPID;
                cs.Zoom = curSettings.Zoom;
                cs.VertOffset = curSettings.VertOffset;
                cs.MaskRatio = curSettings.Mask;

                // check if this number allready exist
                int test = ChannelSettings.List.FindIndex(ChannelSettings.byNumber(cs.ChannelNumber));
                if (test != -1)
                    ChannelSettings.List[test] = cs;
                else
                    ChannelSettings.List.Add(cs);
            }
        }

        /// <summary>
        /// Set channel (called by a timer)
        /// </summary>
        /// <param name="updatepg">Update epg for current channel</param>
        private void TimerChangeChannelDelay_Tick(object sender, EventArgs e)
        {
            TimerChangeChannelDelay.Stop();
            ChannelData cd = (ChannelData)TimerChangeChannelDelay.Tag;
            ChannelData previouscd = new ChannelData(curcd.Ip, curcd.Name, curcd.Num, curcd.Node, curcd.Locked, curcd.Skip);
            prevcd = previouscd;

            // stop
            if (player != null)
            {
                savePerChanSettings(previouscd); // save per channel settings 
                player.Audio.Tracks._ReloadTracks = true;
                player.Subtitles._ReloadTracks = true;
                //player.Video.Parent = IntPtr.Zero;  //important
                // possible deadlock workaround
                forcestop.Reset();
                Tstop = new Thread(tVlcStop);
                Tstop.IsBackground = true;
                Tstop.Start();
                // we wait max 500 ms for thread to stop
                if (!forcestop.WaitOne(500, true))
                {
                    // deadlock  occurred !!
                    stopped.Set();
                    vlc = null;
                    player = null;
                    GC.Collect();
                }
                Tstop = null;

                // make sure that libvlc_stop has finished
                stopped.WaitOne(300, true);
            }

            // Reset stuff
            clearDynamicMenus();
            cleatTtxGui();
            clearStripMenus();
            curSettings.Clear();
            
            loadArgs();
            setVlc(cd); // load per channel settings and start playing
            currentInfo(cd); 

            if (this.Visible)
            {
                //Clear listview
                if (doubleClickFromTreeElapsed)
                    clearListview();
                // Update epg
                if (epg != null && Properties.Settings.Default.EnableEpgplugin && doubleClickFromTreeElapsed)
                    epg.start(cd.Name, new epgResult(cd.Ip,cd.Name,"main"));
            }
        }

        private void setVlc(ChannelData cd)
        {
            if (vlc == null)
                vlc = new VideoLan.VideoLanClient(path, Globals.vlcargs);

            if (player == null)
            {
                player = vlc.NewMediaPlayer(pictureBox1.Handle);

                player.StateChanged += new EventHandler<StateChangedEventArgs>(player_StateChanged);
                player.PositionChanged += new EventHandler<PositionChangedEventArgs>(player_PositionChanged);
                // player.PausableChanged += new EventHandler<PausableChangedEventArgs>(player_PausableChanged);
                // player.SeekableChanged += new EventHandler<SeekableChangedEventArgs>(player_SeekableChanged);
                // player.TimeChanged += new EventHandler<TimeChangedEventArgs>(player_TimeChanged);
            }

            //player.Video.Parent = pictureBox1.Handle; // important

            bool locked = cd.Locked;
            if (locked)
            {
                if (!passwordAlert())
                    return;
            }
            
            TimerStreamInfo.Stop();
            streamInfoCheckCount = 0;
            TimerStreamInfo.Start();

            // Multicast mode
            if (Properties.Settings.Default.Multicast)
            {
                VideoLan.VlcMedia media = vlc.NewMedia(System.Text.Encoding.UTF8.GetBytes("udp://@" + cd.Ip));
                playerArguments(ref media,cd);
                player.Load(media);
                media.Dispose();
                player.Audio.Volume = volume * 10;

                vlcPlay();
            }
            // Unicast mode
            else
            {
                if (Properties.Settings.Default.UnicastServer == "vlc")
                    vlmSetChannel(cd.Ip);
                else if (Properties.Settings.Default.UnicastServer == "udprelay")
                    udprSetChannel(cd.Ip);

                VideoLan.VlcMedia media = vlc.NewMedia(System.Text.Encoding.UTF8.GetBytes("udp://@:" + Properties.Settings.Default.DestPort));
                playerArguments(ref media,cd);
                player.Load(media);
                media.Dispose();
                player.Audio.Volume = volume * 10;

                vlcPlay();
            }
        }

        /// <summary>
        ///Send "clear broadcast element" command to vlc http server.
        /// </summary>
        public void vlmClearChannel(bool wait)
        {
            string vlm = Properties.Settings.Default.VlmName;
            string host = Properties.Settings.Default.DestIp;

            string t1 = "GET /requests/vlm_cmd.xml?command=del+" + vlm + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: \r\nKeep-Alive: \r\n\r\n";
            string[] t = { t1 };

            httpreq.urip = Properties.Settings.Default.VlcIp;
            httpreq.urport = Properties.Settings.Default.VlcPort;
            httpreq.sendreq = t;
            uniChange = new Thread(new ThreadStart(httpreq.Send));
            uniChange.IsBackground = true;
            uniChange.Start();
            if (wait)
                uniChange.Join(5000);
        }

        /// <summary>
        ///Sets vlm element
        /// </summary>
        public void vlmSetChannel(string ip)
        {
            string inputuni = "udp://@" + ip;
            string dest = "udp://" + Properties.Settings.Default.DestIp + ":" + Properties.Settings.Default.DestPort;
            string vlm = Properties.Settings.Default.VlmName;
            string host = Properties.Settings.Default.DestIp;
            string t1 = "GET /requests/vlm_cmd.xml?command=del+" + vlm + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: Keep-Alive\r\nKeep-Alive: 3000\r\n\r\n";
            string t2 = "GET /requests/vlm_cmd.xml?command=new+" + vlm + "+broadcast+enabled HTTP/1.1\r\nHost: " + host + "\r\nConnection: Keep-Alive\r\nKeep-Alive: 3000\r\n\r\n";
            string t3 = "GET /requests/vlm_cmd.xml?command=setup+" + vlm + "+input+" + HttpUtility.UrlEncode(inputuni, Encoding.ASCII) + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: Keep-Alive\r\nKeep-Alive: 3000\r\n\r\n";
            string t4 = "GET /requests/vlm_cmd.xml?command=setup+" + vlm + "+output+" + HttpUtility.UrlEncode(dest, Encoding.ASCII) + " HTTP/1.1\r\nHost: " + host + "\r\nConnection: Keep-Alive\r\nKeep-Alive: 3000\r\n\r\n ";
            string t5 = "GET /requests/vlm_cmd.xml?command=control+" + vlm + "+play HTTP/1.1\r\nHost: " + host + "\r\nConnection: Keep-Alive\r\nKeep-Alive: 3000\r\n\r\n";
            string[] t = { t1, t2, t3, t4, t5 };

            httpreq.sendreq = t;
            uniChange = new Thread(new ThreadStart(httpreq.Send));
            uniChange.IsBackground = true;
            uniChange.Start();
        }
        
        /// <summary>
        ///Udp relay set channel
        /// </summary>
        public void udprSetChannel(string ip)
        {
            udprreq.chan = dvchan.Find(curcd.Num) + 1;
            udprreq.set = true;
            udprreq.urip = Properties.Settings.Default.VlcIp;
            udprreq.urport = Properties.Settings.Default.VlcPort;
            uniChange = new Thread(new ThreadStart(udprreq.setChannel));
            uniChange.IsBackground = true;
            uniChange.Start();
        }

        /// <summary>
        ///Udp relay close channel
        /// </summary>
        public void udprClearChannel(bool wait)
        {
            udprreq.chan = -1;
            udprreq.set = false;
            udprreq.urip = Properties.Settings.Default.VlcIp;
            udprreq.urport = Properties.Settings.Default.VlcPort;
            uniChange = new Thread(new ThreadStart(udprreq.setChannel));
            uniChange.IsBackground = true;
            uniChange.Start();
            if (wait)
                uniChange.Join(5000);
        }

        /// <summary>
        ///Start/stop recording (F7)
        /// </summary>
        /// <param name="play">Start=0,Stop=1</param>
        /// <param name="ip">Multicast ip</param>
        /// <param name="name">File name</praam>
        private void recordnow(int play, string ip, string name)
        {
            if (play == 1)
            {
                DateTime curdate = DateTime.Now;
                string removeInvalidChars = @"[^A-Za-z0-9 ]";
                name = Regex.Replace(name, removeInvalidChars, String.Empty);

                string filename = curdate.ToString("yyyyMMddHHmmss-") + name;

                // vlc
                string udp = "udp://@" + ip;
                string recoption;
                recoption = ":sout=#std{access=file,mux=" + Properties.Settings.Default.RecordingFormat + ",dst=" + recfolder + @"\" + filename + "." + Properties.Settings.Default.RecordingFormat + ".mpg}";

                // init
                vlc_rec = new VideoLan.VideoLanClient(path);
                player_rec = vlc_rec.NewMediaPlayer(new IntPtr());

                VideoLan.VlcMedia media = vlc_rec.NewMedia(System.Text.Encoding.UTF8.GetBytes("udp://@" + ip));
                //media.AddOption(":plugin-path=" + path + @"\plugins\");

                if (!Properties.Settings.Default.RecRaw)
                {
                    media.AddOption(System.Text.Encoding.UTF8.GetBytes(recoption));
                    if (curSettings.AudioPID != -1)
                        media.AddOption(System.Text.Encoding.UTF8.GetBytes(":audio-track-id=" + curSettings.AudioPID.ToString()));
                    if (curSettings.SubtitlesPID != -1)
                        media.AddOption(System.Text.Encoding.UTF8.GetBytes(":sub-track-id=" + curSettings.SubtitlesPID.ToString()));
                }
                else // raw format
                {
                    media.AddOption(System.Text.Encoding.UTF8.GetBytes(":demuxdump-file=" + recfolder + @"\" + filename + ".mpg"));
                    media.AddOption(System.Text.Encoding.UTF8.GetBytes(":demux=dump"));
                }
                //media.AddOption(System.Text.Encoding.UTF8.GetBytes(":ts-dump-size=131072"));

                player_rec.Load(media);
                media.Dispose();
                player_rec.Play();
                GC.Collect();

                // gui:
                label1.Text = "REC: " + name;
                recLabelSize();
                label1.Visible = true;
                label1.BringToFront();
            }

            if (play == 0)
            {
                player_rec.Stop();
                player_rec.Dispose();
                player_rec = null;
                vlc_rec.Dispose();
                vlc_rec = null;
                GC.Collect();

                // gui:
                label1.Visible = false;
                recLabelSize();
                label1.Text = "REC";
            }
        }

        /// <summary>
        ///Volume up
        /// </summary>
        private void vlcVolDown()
        {
            if (player != null && player.State != VlcState.Ended && player.State != VlcState.Stopped)
                player.Audio.Volume = volume * 10;
            
            if (volume == 0)
                toolStripStatusLabelMute.BackgroundImage = Properties.Resources.sound_mute;
            else
                toolStripStatusLabelMute.BackgroundImage = null;
        }

        /// <summary>
        ///Volume down
        /// </summary>
        private void vlcVolUp()
        {
            if (player != null && player.State != VlcState.Ended && player.State != VlcState.Stopped)
                player.Audio.Volume = volume * 10;
            
            if (volume == 0)
                toolStripStatusLabelMute.BackgroundImage = Properties.Resources.sound_mute;
            else
                toolStripStatusLabelMute.BackgroundImage = null;
        }

        /// <summary>
        ///Toggle mute
        /// </summary>
        public void toggleMute()
        {
            if (volume != 0)
            {
                prevvolume = volume;
                if (player != null && player.State != VlcState.Ended && player.State != VlcState.Stopped)
                    player.Audio.Volume = 0;
                volume = 0;
                toolStripStatusLabelMute.BackgroundImage = Properties.Resources.sound_mute;
            }
            else
            {
                volume = prevvolume;
                if (player != null && player.State != VlcState.Ended && player.State != VlcState.Stopped)
                    player.Audio.Volume = volume * 10;
                toolStripStatusLabelMute.BackgroundImage = null;
            }
        }
       
        /// <summary>
        ///Set default ar
        /// </summary>
        private void setAr(string ar)
        {
            if (player != null && player.State == VlcState.Playing && player.HasVout)
            {
                if (ar !="default")
                    player.Video.AspectRatio = ar;
                else
                     player.Video.AspectRatio ="";
            }
            curar = ar;

            // gui:
            uncheckAr();
            arGuiUpdate(curar);
        }

        /// <summary>
        ///Set picture crop ratio
        /// </summary>
          private void setMask(float x, float y, bool updateOsd)
        {
             curSettings.Mask = x / y;
             pictureDimensions();
             // gui:
             uncheckMask();
             maskGuiUpdate(x/y);
             if (updateOsd)
                selectionOSD (MLmessages.Mask + ": " + curSettings.Mask.ToString("0.00"));
        }

        /// <summary>
        ///TESTING FUNCTION f9
        /// </summary>
        private void TestFunction()
        {
            //player.Video.DeInterlace = new Deinterlace(true, Properties.Settings.Default.VideoDeinterlaceV3);
        }

        private void vlcSetZoom(float zoom)
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                if (VoutModule == "OpenGL")
                    pictureDimensions();
                else
                    player.Video.Scale = zoom;
            }
        }

         private void vlcMovePicture(int offset)
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                if (VoutModule == "Direct3d")
                    player.Video.SetVerticaloffset(offset);
                else
                    pictureDimensions();
            }
        }

        private void setDeinterlace(bool enabled)
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                player.Video.DeInterlace = new Deinterlace(enabled, Properties.Settings.Default.VideoDeinterlaceV4);
            }
        }

        /// <summary>
        ///Set audio track
        /// </summary>
        private void setAudioTrack(int id)
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                player.Audio.Track = id;
               
                // Get rid of parenthesis for nicer presentation in toolstrip box
                string name;
                if (player.Audio.Tracks.Current.Name.Contains("["))
                    name = player.Audio.Tracks.Current.Name.Split("[".ToCharArray())[1].Replace("]", "");
                else
                    name = name = player.Audio.Tracks.Current.Name;

                 // gui:
                toolStripStatusGeneralInfo.Text = MLmessages.AudioTrack + ": " + name;
                notifyOSD("Audio: " + name);
                int audId = player.Audio.Tracks.Current.ID;
                updateSelectedAudioTrack(audId);

                // update current pids
                curSettings.AudioPID= audId;
            }
        }

        /// <summary>
        ///Set subtitles track
        /// </summary>
        private void setSubtitles(int id, string name)
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                player.Subtitles.Current = new VlcTrack(id, name);

                //Get rid of parenthesis for nicer presentation in toolstrip box
                string desc;
                if (player.Subtitles.Current.Name.Contains("["))
                    desc = player.Subtitles.Current.Name.Split("[".ToCharArray())[1].Replace("]", "");
                else
                    desc = player.Subtitles.Current.Name;

                // gui:
                toolStripStatusGeneralInfo.Text = MLmessages.Subtitle + ": " + desc;
                notifyOSD(MLmessages.Subtitle + ": " + desc);
                int subId = player.Subtitles.Current.ID;
                checkSelectedSubtitle(subId);
                // update current pids
                curSettings.SubtitlesPID = subId;
            }        
        }

        private int getCurrentAudioTrackId()
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                try
                {
                    return player.Audio.Tracks.Current.ID;
                }
                catch { }
            }
            return 0;
        }

        private int getCurrentSubsId()
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                try
                {
                    return player.Subtitles.Current.ID;
                }
                catch { }
            }
            return 0;
        }

        /// <summary>
        ///Cycle through audio languages
        /// </summary>
        private void cycleAudioLanguages()
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                //get number of audio tracks
                int audiotracks, currenttrack;
                try
                {
                    audiotracks = player.Audio.TrackCount;
                    currenttrack = player.Audio.Track;
                }
                catch 
                {
                    // no audio tracks available
                    return;
                }

                if (audiotracks > 2)
                {
                    // now cycle 
                    if (currenttrack + 1 >= audiotracks)
                    {
                        setAudioTrack(0);
                        currenttrack = 0;
                    }
                    else
                    {
                        setAudioTrack(currenttrack + 1);
                        currenttrack++;
                    }

                    // try to get number of audio channels
                    AudioOutputChannel oc = AudioOutputChannel.Error;
                    try
                    {
                        oc = player.Audio.Channel;
                    }
                    catch
                    { }
                    // gui:
                    guiStatusStripAudioChannels(oc);
                }
                else
                {
                    // gui:
                    toolStripStatusGeneralInfo.Text = MLmessages.OneAudioTrack;
                    notifyOSD("Audio:");
                }
            }
        }

        /// <summary>
        ///Cycle through subtitles
        /// </summary>
        private void cycleSubtitles()
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                // get number of subtitles
                int subtracks;
                try
                {
                    subtracks = player.Subtitles.Count;
                }
                catch
                {

                    return; // no subtitles
                }

                if (subtracks > 1)
                {
                    List<int> subId = new List<int>();
                    foreach (VlcTrack tr in player.Subtitles)
                    {
                        subId.Add(tr.ID);
                    }
                    // Find id index
                    int index = subId.IndexOf(player.Subtitles.Current.ID);
                    if (index != -1 && index+1 < player.Subtitles.Count)
                        setSubtitles(index+1,player.Subtitles[index+1].Name);
                    else if (index != -1)
                        setSubtitles(0,player.Subtitles[index].Name);
                }
                else
                    notifyOSD(MLmessages.Subtitle);
            }
        }

        /// <summary>
        ///Get number of audio tracks (called by timer)
        ///TODO: Should be done with events
        /// </summary>
        private void upddateStreamInfo()
        {
            streamInfoCheckCount++;
            if (streamInfoCheckCount > 5)
            {
                TimerStreamInfo.Stop(); // stop timer
                streamInfoCheckCount = 0;
            }
            // maximum checks
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                int x = 0;
                int y = 0;
                
                // At this time audio and subtitle tracks are most probably available.
                // radio ???
                if (player.HasVout)
                {
                    try
                    {
                        x = player.Video.Width;
                        y = player.Video.Height;
                    }
                    catch { return; }
                }
                else
                {
                    return;
                }

                int audiotracks, subtracks;
                AudioOutputChannel ac = new AudioOutputChannel();
                ac = AudioOutputChannel.Error;
                try
                {
                    audiotracks = player.Audio.Tracks.Count;
                }
                catch
                {
                    audiotracks = -1;
                } 
               
                try
                {
                    subtracks = player.Subtitles.Count;
                }
                catch 
                {
                    subtracks = -1;
                }
                try
                {
                    ac = player.Audio.Channel;
                }
                catch
                {

                }

                // audio tracks
                if (audiotracks > 1 )
                {
                    string audioName="";
                    int audioId;
                    try
                    {
                        audioId = player.Audio.Tracks.Current.ID;
                    }
                    catch{ return;}

                    if (player.Audio.Tracks.Current.Name.Contains("["))
                        audioName = player.Audio.Tracks.Current.Name.Split("[".ToCharArray())[1].Replace("]", "");
                    else
                        audioName = player.Audio.Tracks.Current.Name;
                    // update current pids
                    curSettings.AudioPID = audioId;

                    //  gui:
                    // gtr holds also track position not just id
                    List<TrackGui> gtra = new List<TrackGui>();
                    // populate menu
                    int i=0;
                    foreach (VlcTrack tr in player.Audio.Tracks)
                    { 
                        gtra.Add(new TrackGui(i,tr.ID,tr.Name));
                        i++;
                    }
                    dynamicAudioTracksMenu(gtra);
                    updateSelectedAudioTrack(audioId);

                    // subtitles
                    string subName = "";
                    int subId=-1;
                    if (subtracks > 1)
                    {
                        try
                        {
                            subId = player.Subtitles.Current.ID;
                        }
                        catch { return; }

                        if (player.Subtitles.Current.Name.Contains("["))
                            subName = player.Subtitles.Current.Name.Split("[".ToCharArray())[1].Replace("]", "");
                        else 
                            subName = player.Subtitles.Current.Name;
                       
                        // update current pids
                        curSettings.SubtitlesPID = subId;
                        
                        // gui:
                        // update dynamic menu
                        guiSubtitlesMenuUpdate();
                        // update checkboxes 
                        checkSelectedSubtitle(subId);
                    }
                    
                    //Audio languages or subtitles in toolstrip box general info area:
                    if (audiotracks >2 || subtracks >2)
                         toolStripStatusGeneralInfo.Text = MLmessages.AudioTrack+ ": " + audioName + ", " + MLmessages.Subtitle +": " + subName;
                }

                if (Properties.Settings.Default.ShowStatusStripDetails)
                {
                    //gui: statustrip
                    if (subtracks - 1 > 0)
                        toolStripStatusSubsCount.Text = MLmessages.SubtitleAbb + ":" + (Math.Max(0, subtracks - 1)).ToString();
                    toolStripStatusAudioCount.Text = MLmessages.AudioAbb + ":" + (Math.Max(0, audiotracks - 1)).ToString();
                }

                //gui : number of audio channels
                guiStatusStripAudioChannels(ac);
                //gui : video dimensions
                guiStatusStripVideoDimensions(x,y);

                TimerStreamInfo.Stop(); // stop timer
                streamInfoCheckCount = 0;
            } 
        }

        private void guiSubtitlesMenuUpdate()
        {
            List<TrackGui> gtrs = new List<TrackGui>();
            // populate menu
            int j = 0;
            foreach (VlcTrack tr in player.Subtitles)
            {
                gtrs.Add(new TrackGui(j, tr.ID, tr.Name));
                j++;
            }
            dynamicSubsMenu(gtrs);
        }

        private void guiStatusStripAudioChannels(AudioOutputChannel ac)
        {
            // Whati is rStereo and why there is no lStereo ?
            string text = "";
            if (ac == AudioOutputChannel.Error)
                   text = "";
            else if (ac == AudioOutputChannel.Dolbys)
                text = "Dolby";

            else if (ac == AudioOutputChannel.Stereo)
                text = "Stereo";

            else if (ac == AudioOutputChannel.Left || ac == AudioOutputChannel.Right)
                text = "Mono";
            
            if (Properties.Settings.Default.ShowStatusStripDetails)
                toolStripStatusLabelAudioChannels.Text = text;
        }

        private void guiStatusStripVideoDimensions(int x, int y)
        {
            if (y != 0)
            {
                float fx = (float)x;
                float fy = (float)y;
                float r = fx / fy;

                string ar = "";
                if (r > 1.2f && r < 1.4f)
                    ar = "4:3";

                else if (r > 1.75f && r < 1.80f)
                    ar = "16:9";

                else if ((float)Math.Round(r,2)==1.56f)
                    ar = "14:9";

                else if ((float)Math.Round(r,2)==1.60f)
                    ar = "16:10";

                if (Properties.Settings.Default.ShowStatusStripDetails)
                    toolStripStatusLabelVideoInfo.Text = x.ToString()+"x"+y.ToString()+" "+ar;
            }
        }

        /// <summary>
        ///Teletext toggle
        /// </summary>
        private void toggleTTX()
        {
            if (player != null && player.State != VlcState.Ended && player.State != VlcState.Stopped && player.HasVout)
            {
                player.Video.ToggleTeleText();
                if (!label2.Visible)
                {
                    label2.Visible = true;
                    label2.BringToFront();
                    teletextMode = true;
                    cttx = (short) TTX.ON;
                }
                else
                {
                    if (cttx == (short)TTX.ON)
                    {
                        cttx = (short)TTX.TRANSPARENT;
                    }
                    else if (cttx == (short)TTX.TRANSPARENT)
                    {
                        label2.Visible = false;
                        teletextMode = false;
                        cttx = (short)TTX.OFF;
                        label2.Text = "100";
                    }
                }
            }
        }

        /// <summary>
        ///Set teletext page
        /// </summary>
        private void setTeletext(int page)
        {
            if (player != null && player.State != VlcState.Ended && player.State != VlcState.Stopped && player.HasVout)
            {
                player.Video.TeleText = page;
            }
       }

        /// <summary>
        ///Snapshot
        /// </summary>
        private void takeSnapshot()
        {
            if (player != null && player.State != VlcState.Ended && player.State != VlcState.Stopped && player.HasVout)
            {
                DateTime t = DateTime.Now;
                string filename =  curcd.Name;
                filename = Epg.validateFileName(filename);
                filename =   filename + "-"+ t.ToString("d-MM-yy");
                if (filename.Length > 28)
                    filename = filename.Substring(0, 28);

                Properties.Settings.Default.LastSnapShotnUM++;
                string name = Properties.Settings.Default.RecFolder+@"\"+filename+"_"+Properties.Settings.Default.LastSnapShotnUM+".png";
                player.Video.TakeSnapShot(System.Text.Encoding.UTF8.GetBytes(name), (uint)player.Video.Width, (uint)player.Video.Height);
            }
        }

        /// <summary>
        ///Mute
        /// </summary>
        private void muteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            toggleMute();
        }

        /// <summary>
        ///Stop playing
        /// </summary>
        public void vlcStop()
        {
            // GUI reset
            clearDynamicMenus();
            cleatTtxGui();
            clearStripMenus();
            guiSetPause(false);

            savePerChanSettings(curcd);
            try
            {
                if (player != null)
                {
                    if (player.State == VlcState.Playing)
                    {
                        forcestop.Reset();
                        Tstop = new Thread(tVlcStop);
                        Tstop.IsBackground = true;
                        Tstop.Start();
                        if (!forcestop.WaitOne(1000, true))
                        {
                            // deadlock  occurred !!
                            stopped.Set();
                            vlc = null;
                            player = null;
                            GC.Collect();
                        }
                        Tstop = null;
                    }
                   
                    if (Properties.Settings.Default.ScreenSaver)
                    {
                       enableScreenSaver();
                    }
                }
                if (player != null)
                    player.Dispose();
                player = null;

                // make sure that libvlc_stop has finished
                stopped.WaitOne(500, true);

             }   
             catch { }

            // if unicast was selected and vlc is responding then we must clear channel on exit
            if (!Properties.Settings.Default.Multicast )
            {
                if (Properties.Settings.Default.UnicastServer == "vlc")
                    vlmClearChannel(false);
                else if (Properties.Settings.Default.UnicastServer == "udprelay")
                    udprClearChannel(false);
            }
        }

        public void tVlcStop()
        {
            if (player != null && player.State == VlcState.Playing)
            {
                player.Stop();
            }
            forcestop.Set();
        }

        public void vlcDispose()
        {
           if (vlc != null)
               vlc.Dispose();
            vlc = null;
            //GC.Collect();
        }

        public void vlcPlay()
        {
            if (player != null)
            {
                try
                {
                    player.Play();
                }
                catch { }
                if (Properties.Settings.Default.ScreenSaver)
                {
                    preventScreenSaver();
                }
                // gui:
                guiSetPause(false);
                if (OsdInput == MLmessages.Pause)
                    OsdHideWindow(); // hide pause osd
            }
        }

        /// <summary>
        ///Play/Pause
        /// </summary>
        private void vlcpause()
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                player.Pause();
                if (Properties.Settings.Default.ScreenSaver)
                    enableScreenSaver();
                // gui:
                notifyOSD(MLmessages.Pause);
                guiSetPause(true);
            }
            else if (vlc != null && player != null && (player.State == VlcState.Ended || player.State == VlcState.Paused))
                vlcPlay();
        }

        /// <summary>
        ///Play faster
        /// </summary>
        private void vlcfaster()
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                float rate;
                rate = (float)Math.Round(player.Rate, 2);
                rate = Math.Min(10.0f, rate + 0.25f);
                if (rate < 1.24f && rate > 0.76f)
                    rate = 1.0f;

                player.Rate = rate;

                if (player.Rate != 1.0f)
                    player.Audio.Volume = 5;
                else
                    player.Audio.Volume = volume * 10;

                notifyOSD(MLmessages.Rate + ": " + rate.ToString());
                if (rate != 1)
                    toolStripStatusGeneralInfo.Text = MLmessages.Rate + ": " + rate.ToString();
                else
                    toolStripStatusGeneralInfo.Text = "";
            }
        }

        /// <summary>
        ///Play slower
        ///TODO: Play backwards
        /// </summary>
        private void vlcslower()
        {
            if (vlc != null && player != null && player.State == VlcState.Playing)
            {
                float rate;
                rate = (float)Math.Round(player.Rate, 2);
                rate = Math.Max(0.25f, rate - 0.25f);
                if (rate < 1.20f && rate > 0.80f)
                    rate = 1;

                player.Rate = rate;

                if (player.Rate != 1.0f)
                    player.Audio.Volume = 5;
                else
                    player.Audio.Volume = volume * 10;

                notifyOSD(MLmessages.Rate + ": " + rate.ToString());
                
                if (rate != 1)
                    toolStripStatusGeneralInfo.Text = MLmessages.Rate + ": " + rate.ToString();
                else
                    toolStripStatusGeneralInfo.Text = "";
            }
        }

        /// <summary>
        ///Vlc events
        /// </summary>
        private void player_StateChanged(object sender, StateChangedEventArgs e)
        {
            switch (e.NewState)
            {
                case VlcState.Ended:
                    stopped.Set();
                    break;

                case VlcState.Error:
                   
                   //Handle playing error
                    if (player != null)
                    {
                        player.Dispose();
                        player = null;
                    }
                    stopped.Set();
                   
                    break;
                case VlcState.Buffering:

                    break;
                case VlcState.Playing:

                    stopped.Reset();
                    
                    break;
                case VlcState.Opening:

                    break;

                case VlcState.Paused:

                    break;

                case VlcState.Stopped:

                     stopped.Set();
                    break;
            }
        }

        private void player_PositionChanged (object sender, PositionChangedEventArgs e)
        {
            // TODO: timeshift mode - detect end of stream and set volume to original.  
        }

        private  void player_PausableChanged(object sender, PausableChangedEventArgs e)
        {
           // bool test = e.IsSeekable;
        }

        void player_SeekableChanged(object sender, SeekableChangedEventArgs e)
        {
           // bool test = e.IsSeekable;
        }

        void player_TimeChanged(object sender, TimeChangedEventArgs e)
        {
            //long time = e.Time;
        }

        public void preventScreenSaver()
        {
           SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, false, 0, 0);
           preventMonitorOff();
        }

        public void enableScreenSaver()
        {
            SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, true, 0, 0);
            enableMonitorOff();
        }

         private void preventMonitorOff()
         {
             if (Globals.RecInstanceCount==0)
             {
                SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS | EXECUTION_STATE.ES_SYSTEM_REQUIRED);
             }
         }

         private void enableMonitorOff()
         {
             if (Globals.RecInstanceCount == 0)
             {
                 SetThreadExecutionState(EXECUTION_STATE.ES_CONTINUOUS);
             }
         }

        private void guiSetPause(bool set)
        {
            if (set)
                toolStripStatusLabelPause.BackgroundImage = Properties.Resources.control_pause;
            else
                toolStripStatusLabelPause.BackgroundImage = null;
        }
        
        #endregion

    }

    public class ChanSettings
    {
        private int _videoPID = -1;
        private int _audioPID = -1;
        private int _subtitlesPID = -1;
        private float _zoom = 1.0f;
        private float _mask = 1.0f;
        private int _vertOffset = 0;

        public ChanSettings()
        {
            Clear();
        }
        
        public int VideoPID 
        {
            get { return _videoPID; }
            set { _videoPID = value; }        
        }

        public int AudioPID
        {
            get { return _audioPID; }
            set { _audioPID = value; }        
        }

        public int SubtitlesPID
        {
            get { return _subtitlesPID; }
            set { _subtitlesPID = value; }
        }

        public float Zoom
        {
            get { return _zoom; }
            set { _zoom = value; }
        }

        public float Mask
        {
            get { return _mask; }
            set { _mask = value; }
        }

        public int VertOffset
        {
            get { return _vertOffset; }
            set { _vertOffset = value; }
        }
        
        public void Clear()
        {
            _videoPID = -1;
            _audioPID = -1;
            _subtitlesPID = -1;
            _zoom = 1.0f;
            _mask = 1.0f;
            _vertOffset = 0;
        }
    }
}
